/*
 * Copyright 2011 Angel Sanadinov
 *
 * This file is part of VBox WMI.
 *
 * VBox WMI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VBox WMI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VBox WMI.  If not, see <http://www.gnu.org/licenses/>.
 */

package Services;

import Beans.Requests.RequestBean;
import Beans.Requests.ServerManagementRequest;
import Beans.Requests.SimpleAuthorizationRequest;
import Beans.Responses.ServerManagementResponse;
import Beans.ServerBean;
import Managers.DatabaseManager;
import Managers.LogsManager;
import Managers.PermissionsManager;
import Managers.Utilities.DatabaseManipulationResult;
import Managers.Utilities.RequestResponseWrapper;
import Managers.Utilities.ResponseMessagesContainer;
import Managers.VBoxConnectionsManager;
import Utilities.Constants;
import Utilities.ApplicationException;
import Utilities.ExceptionMessages;
import Utilities.ParameterNames;
import Utilities.PermissionsConstants;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * This servlet represents the application's Servers Management Service. <br><br>
 *
 * This service takes care of the servers creation, removal and modification on the
 * system.
 *
 * @author Angel Sanadinov
 */
public class ServersManagementService extends HttpServlet
{
    LogsManager logsManager;                     //logs manager
    DatabaseManager databaseManager;             //database manager
    PermissionsManager permissionsManager;       //permissions manager
    VBoxConnectionsManager connectionsManager;   //virtualbox connections manager

    /**
     * Initializes the service and retrieves all necessary managers.
     *
     * @throws ServletException if an exception occurs that interrupts the servlet's
     *                          normal operation
     *
     * @see HttpServlet#init()
     */
    @Override
    public void init() throws ServletException
    {
        ServletContext context = getServletContext();
        logsManager = (LogsManager)context.getAttribute(Constants.CONTEXT_LOGS_MANAGER);
        databaseManager = (DatabaseManager)context.getAttribute(Constants.CONTEXT_DATABASE_MANAGER);
        permissionsManager = (PermissionsManager)context.getAttribute(Constants.CONTEXT_PERMISSIONS_MANAGER);
        connectionsManager = (VBoxConnectionsManager)context.getAttribute(Constants.CONTEXT_VBOX_CONNECTIONS_MANAGER);
    }

    /**
     * Shuts down the service and cleans up any object references.
     *
     * @see HttpServlet#destroy()
     */
    @Override
    public void destroy()
    {
        //log shutdown of service?
        logsManager = null;
        databaseManager = null;
        permissionsManager = null;
        connectionsManager = null;
    }

    /**
     * Processes a <code>GET</code> request from a client.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request for the GET could not be handled
     * @throws IOException if an input or output error is detected when the servlet
     *         handles the GET request
     *
     * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Processes a <code>POST</code> request from a client.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request for the POST could not be handled
     * @throws IOException if an input or output error is detected when the servlet
     *         handles the POST request
     *
     * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Processes all <code>GET</code> and <code>POST</code> requests and carries out
     * operations based on requested actions.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @throws ServletException if the request could not be handled
     * @throws IOException if an input or output error is detected when the service
     *         handles the request
     */
    private void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        //retrieves the server management request
        ServerManagementRequest manageServerRequest =
                (ServerManagementRequest)request.getAttribute(ParameterNames.REQUEST_MANAGE_SERVER);

        //checks that the request is available and is valid
        if(manageServerRequest != null && manageServerRequest.isValid())
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                           "Request: " + manageServerRequest.toString());
            //attempts to authorize the request
            boolean isActionAuthorized = permissionsManager.isActionAuthorized(
                    new SimpleAuthorizationRequest(manageServerRequest.getRequestorId(),
                                                   PermissionsConstants.BASIC_ACTION_MANAGE_SERVER));

            //checks if the request is authorized...
            if(isActionAuthorized)
            {
                //carries out the appropriate action based on the request data
                switch(manageServerRequest.getRequestedAction())
                {
                    //a new server is to be created
                    case PermissionsConstants.ACTION_SERVER_CREATE:
                    {
                        processResponse(request, actionCreateServer(manageServerRequest));
                    } break;

                    //an existing server is to be modified
                    case PermissionsConstants.ACTION_SERVER_MODIFY:
                    {
                        processResponse(request, actionModifyServer(manageServerRequest));
                    } break;

                    //an existing server is to removed
                    case PermissionsConstants.ACTION_SERVER_REMOVE:
                    {
                        processResponse(request, actionRemoveServer(manageServerRequest));
                    } break;

                    default:
                    {
                        processResponse(request, new ServerManagementResponse(false, 
                                new ApplicationException(ExceptionMessages.APP_E_INVALID_REQUESTED_ACTION)));
                    } break;
                }
            }
            else
            {
                processResponse(request, new ServerManagementResponse(false, 
                        new ApplicationException(ExceptionMessages.APP_E_OPERATION_NOT_ALLOWED)));
            }
        }
        else
        {
            processResponse(request, new ServerManagementResponse(false, 
                    new ApplicationException(ExceptionMessages.APP_E_INVALID_REQUEST)));
        }
    }

    /**
     * Processes all responses going out of the service.
     *
     * @param request an HttpServletRequest object that contains the request the
     *                client has made of the servlet
     * @param response an HttpServletResponse object that contains the response
     *                 the servlet sends to the client
     * @param serverResponse server management response object
     * @throws ServletException if the processing of the response could not be handled
     * @throws IOException if an input or output error is detected when the response
     *                     is handled
     */
    private void processResponse(HttpServletRequest request, ServerManagementResponse serverResponse)
            throws ServletException, IOException
    {
        //retrieves the request object from the session
        RequestBean requestData = (RequestBean)request.getAttribute(ParameterNames.REQUEST_MANAGE_SERVER);
        request.setAttribute(ParameterNames.REQUEST_MANAGE_SERVER, null);
        request.setAttribute(ParameterNames.RESPONSE_MANAGE_SERVER, serverResponse);

        //checks if the requested action failed
        if(!serverResponse.getRequestResult())
        {
            //the request failed; retrieves the message container and stores the request/response pair
            ResponseMessagesContainer messagesContainer = (ResponseMessagesContainer)
                    request.getSession(false).getAttribute(ParameterNames.SESSION_REQUEST_MESSAGES_OBJECT);

            messagesContainer.put(new RequestResponseWrapper(requestData, serverResponse));

            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_DEBUG,
                                       "Request execution failed: " + serverResponse.getException().toString());
        }
        else
            ;
    }

    /**
     * Creates a new server and adds its data to the database.
     *
     * @param request the server management request
     * @return a response to the server management request
     */
    private ServerManagementResponse actionCreateServer(ServerManagementRequest request)
    {
        ApplicationException exception = null;  //operation exception
        boolean responseStatus = false; //operation response status

        //attempts to add the server to the database
        DatabaseManipulationResult result =
                databaseManager.insertServer(new ServerBean(request.getName(),
                                                            request.getDescription(),
                                                            request.getWebServiceAddress(),
                                                            request.getWebServiceUser(),
                                                            request.getWebServicePassword(),
                                                            request.getSharedFoldersRoot(),
                                                            request.getVrdpPortsRangeLow(),
                                                            request.getVrdpPortsRangeHigh(),
                                                            request.getVrdpPortsPerMachine()));

        //checks that a valid DMR was returned
        if(result != null)
        {
            if(result.wasOperationSuccessful()) //was the operation successful?
            {
                responseStatus = true;
                //logs the server addition
                logsManager.log(System.currentTimeMillis(), Constants.LOG_SEVERITY_INFORMATION,
                                "Server " + request.getName() + " was created", request.getRequestorId());

                //notifies the connections manager of the addition
                ServerBean newServer = databaseManager.getServer(request.getWebServiceAddress());
                if(newServer != null)
                    connectionsManager.updateConnections(newServer.getServerId());
                else
                    ;
            }
            else if(result.getErrorCode() == Constants.SQL_ERROR_ENTRY_EXISTS)
                exception = new ApplicationException(ExceptionMessages.APP_E_SERVER_EXISTS, request.getWebServiceAddress());
            else
            {
                exception = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                     "(" + result.getErrorCode() + ")");
            }
        }
        else
            exception = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION, "(N/A)");

        return new ServerManagementResponse(responseStatus, exception);
    }

    /**
     * Modifies an existing server.
     *
     * @param request the server management request
     * @return a response to the server management request
     */
    private ServerManagementResponse actionModifyServer(ServerManagementRequest request)
    {
        ApplicationException exception = null;  //operation exception
        boolean responseStatus = false; //operation response status

        //attempts to update the database with the new server data
        DatabaseManipulationResult result =
                databaseManager.updateServer(new ServerBean(request.getServerId(),
                                                            null,
                                                            request.getName(),
                                                            request.getDescription(),
                                                            request.getWebServiceAddress(),
                                                            request.getWebServiceUser(),
                                                            request.getWebServicePassword(),
                                                            request.getSharedFoldersRoot(),
                                                            request.getVrdpPortsRangeLow(),
                                                            request.getVrdpPortsRangeHigh(),
                                                            request.getVrdpPortsPerMachine(),
                                                            null));

        //checks that a valid DMR was returned
        if(result != null)
        {
            if(result.wasOperationSuccessful())//was the operation successful?
            {
                responseStatus = true;
                //notifies the connections manager of the modification
                connectionsManager.updateConnections(request.getServerId());
                //logs the server modification
                logsManager.log(System.currentTimeMillis(), Constants.LOG_SEVERITY_INFORMATION,
                                "Modified server " + request.getServerId(), request.getRequestorId());
            }
            else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_UPDATED)
            {
                exception = new ApplicationException(ExceptionMessages.APP_E_SERVER_MODIFICATION_FAILED,
                                                     Integer.toString(request.getServerId()));
            }
            else
            {
                exception = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                     "(" + result.getErrorCode() + ")");
            }
        }
        else
            exception = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION, "(N/A)");

        return new ServerManagementResponse(responseStatus, exception);
    }

    /**
     * Removes an existing server.
     *
     * @param request the server management request
     * @return a response to the server management request
     */
    private ServerManagementResponse actionRemoveServer(ServerManagementRequest request)
    {
        //attempts to remove the server from the database
        DatabaseManipulationResult result = databaseManager.removeServer(request.getServerId());
        ApplicationException exception = null;  //operation exception
        boolean responseStatus = false; //operation response status

        //checks that a valid DMR was returned
        if(result != null)
        {
            if(result.wasOperationSuccessful())//was the operation successful?
            {
                responseStatus = true;
                //logs the server removal
                logsManager.log(System.currentTimeMillis(), Constants.LOG_SEVERITY_INFORMATION,
                            "Server " + request.getServerId() + " was removed", request.getRequestorId());

                //notifies the connections manager of the removal
                connectionsManager.updateConnections(request.getServerId());
            }
            else if(result.getErrorCode() == Constants.SQL_ERROR_NOT_REMOVED)
            {
                exception = new ApplicationException(ExceptionMessages.APP_E_SERVER_REMOVAL_FAILED,
                                                     Integer.toString(request.getServerId()));
            }
            else
            {
                exception = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION,
                                                     "(" + result.getErrorCode() + ")");
            }
        }
        else
            exception = new ApplicationException(ExceptionMessages.APP_E_DATABASE_EXCEPTION, "(N/A)");

        return new ServerManagementResponse(responseStatus, exception);
    }
}
